package com.jprocms.module.cms.service.content;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.jpro.framework.common.pojo.CommonResult;
import com.jpro.framework.common.util.json.JsonUtils;
import com.jpro.module.system.api.sensitiveword.SensitiveWordApi;
import com.jpro.module.system.api.sensitiveword.dto.SensitiveWordDto;
import com.jprocms.module.cms.controller.admin.content.vo.ContentBaseVO;
import com.jprocms.module.cms.controller.admin.content.vo.SensitiveWordCheckVO;
import com.jprocms.module.cms.controller.admin.sensitivetask.vo.TaskSensitiveResultCreateReqVO;
import com.jprocms.module.cms.convert.content.ContentAttrConvert;
import com.jprocms.module.cms.convert.content.ContentConvert;
import com.jprocms.module.cms.dal.dataobject.channel.ChannelDO;
import com.jprocms.module.cms.dal.dataobject.content.ContentAttrDO;
import com.jprocms.module.cms.dal.dataobject.content.ContentDO;
import com.jprocms.module.cms.dal.dataobject.globalconfig.GlobalConfigDO;
import com.jprocms.module.cms.dal.dataobject.model.ModelDO;
import com.jprocms.module.cms.dal.dataobject.site.SiteDO;
import com.jprocms.module.cms.dal.dataobject.task.TaskDO;
import com.jprocms.module.cms.dal.mysql.channel.ChannelMapper;
import com.jprocms.module.cms.dal.mysql.model.ModelMapper;
import com.jprocms.module.cms.enums.EnumContentStatus;
import com.jprocms.module.cms.enums.EnumSensitiveWordProcessModel;
import com.jprocms.module.cms.service.channel.ChannelService;
import com.jprocms.module.cms.service.globalconfig.GlobalConfigService;
import com.jprocms.module.cms.service.sensitivetask.SensitiveTaskService;
import com.jprocms.module.cms.service.staticpage.AbstractProducer;
import com.jprocms.module.cms.service.staticpage.StaticHtmlService;
import com.jprocms.module.cms.service.task.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.BiConsumer;

import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.jpro.module.system.api.sensitiveword.SensitiveWordApi.SCENE_TAG_CONTENT_LIST;
import static com.jprocms.module.cms.config.ThreadPoolConfiguration.CMS_CHECK_SENSITIVEWORD_THREAD_POOL_TASK_EXECUTOR;
import static com.jprocms.module.cms.enums.ErrorCodeConstants.SENSITIVEWORD_FORBID;
import static com.jprocms.module.cms.util.ModelWrapper.*;

@Slf4j
@Service
public class SensitiveWordService extends AbstractProducer {
    private SensitiveTaskService sensitiveTaskService;
    protected SensitiveWordService(ContentService contentService, ChannelService channelService, StaticHtmlService htmlService,
                                   TaskService taskService, SensitiveTaskService sensitiveTaskService,
                                   @Qualifier(CMS_CHECK_SENSITIVEWORD_THREAD_POOL_TASK_EXECUTOR) ThreadPoolTaskExecutor executor) {
        super(contentService, taskService, executor);
        this.sensitiveTaskService = sensitiveTaskService;
    }

    /**
     * 扫描当前站点所有内容是否包含敏感词
     *
     * @param taskSiteId 任务站点ID
     * @param taskUserId 任务用户ID
     * @param taskName   任务名称
     * @param site       需要生成静态页的站点
     */
    public void checkContentSensitiveWord(Long taskSiteId, Long taskUserId, String taskName, SiteDO site) {
        execute(taskSiteId, taskUserId, taskName, TaskDO.TYPE_CHECK_SENSITIVE_WORD, false,
                taskId -> {
                    handleContent(taskId, site.getId(), this::checkContent);
                });
    }

    public void checkContent(Long taskId, ContentDO contentDO) {
        final ContentBaseVO contentBaseVO = ContentConvert.INSTANCE.convertForBaseVO(contentDO);
        final Set<String> sensitiveWordSet = checkSensitiveWord(contentBaseVO);
        if (CollUtil.isNotEmpty(sensitiveWordSet)) {
            TaskSensitiveResultCreateReqVO resultCreateReqVO = new TaskSensitiveResultCreateReqVO();
            resultCreateReqVO.setContentId(contentDO.getId());
            resultCreateReqVO.setSiteId(contentDO.getSiteId());
            resultCreateReqVO.setTaskId(taskId);
            resultCreateReqVO.setSensitiveWord(JsonUtils.toJsonString(sensitiveWordSet));
            sensitiveTaskService.createTaskSensitiveResult(resultCreateReqVO);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    void handleContent(Long taskId, Long siteId, BiConsumer<Long, ContentDO> consumer) {
        Long minId = 0L;
        int offset = 0;
        int limit = 100;
        int size = limit;
        //从最小id查询，每100条一次查询，>=100 说明还有下一页数据需要继续处理，minId继续查询
        while (size >= limit) {
            List<ContentDO> list = contentService.getListByMinId(minId, siteId, EnumContentStatus.PUBLISHED.getStatus(), offset, limit);
            size = list.size();
            for (int i = 0; i < size; i++) {
                ContentDO content = list.get(i);
                consumer.accept(taskId, content);
                if (i == size - 1) {
                    minId = content.getId();
                }
            }
            if (updateTask(taskId, size)) {
                break;
            }
        }
    }

    /**
     * 检查内容 是否有敏感词
     *
     * @param contentBaseVO
     * @return
     */
    public Set<String> checkSensitiveWord(ContentBaseVO contentBaseVO) {
        Set<String> sensitiveWords = new HashSet<>();
        final GlobalConfigDO defGlobalConfig = globalConfigService.getDefGlobalConfig();
        final int sensitiveWordProcessModel = defGlobalConfig.getOtherConfig().getSensitiveWordProcessModel();
        //关闭
        if (EnumSensitiveWordProcessModel.CLOSE.getType().equals(sensitiveWordProcessModel)) {
            return sensitiveWords;
        } else if (EnumSensitiveWordProcessModel.TIP_REPLACE.getType().equals(sensitiveWordProcessModel)
                || EnumSensitiveWordProcessModel.FORBID.getType().equals(sensitiveWordProcessModel)) {
            //不允许提交或者提示类则取检查 返回有的敏感词
            sensitiveWords = getSensitiveWords(contentBaseVO);
        }
        return sensitiveWords;
    }

    /**
     * 检查内容字段 是否有敏感词
     *
     * @param wordCheckVO
     * @return
     */
    public Set<String> checkSensitiveWord(SensitiveWordCheckVO wordCheckVO) {
        Set<String> sensitiveWords = new HashSet<>();
        final GlobalConfigDO defGlobalConfig = globalConfigService.getDefGlobalConfig();
        final int sensitiveWordProcessModel = defGlobalConfig.getOtherConfig().getSensitiveWordProcessModel();
        //关闭
        if (EnumSensitiveWordProcessModel.CLOSE.getType().equals(sensitiveWordProcessModel)) {
            return sensitiveWords;
        } else if (EnumSensitiveWordProcessModel.TIP_REPLACE.getType().equals(sensitiveWordProcessModel)
                || EnumSensitiveWordProcessModel.FORBID.getType().equals(sensitiveWordProcessModel)) {
            //不允许提交或者提示类则取检查 返回有的敏感词
            final CommonResult<List<String>> validResult = sensitiveWordApi.validateText((new SensitiveWordDto(wordCheckVO.getText(), SCENE_TAG_CONTENT_LIST)));
            if (validResult.isSuccess()) {
                sensitiveWords.addAll(validResult.getData());
            }
        }
        return sensitiveWords;
    }

    /**
     * 目前只处理了标题 描述 系统正文 自定义字段的文本类的字段
     * 检查并处理敏感词，如果配置的关闭则不处理
     * 如果配置不允许提交则抛出异常
     * 如果配置需要替换则将替换后的对象返回
     *
     * @param createReqVO
     * @return
     */
    public ContentBaseVO checkAndProcessSensitiveWord(ContentBaseVO createReqVO) {
        final GlobalConfigDO defGlobalConfig = globalConfigService.getDefGlobalConfig();
        //关闭
        if (EnumSensitiveWordProcessModel.CLOSE.getType().equals(defGlobalConfig.getOtherConfig().getSensitiveWordProcessModel())) {
            return createReqVO;
        } else if (EnumSensitiveWordProcessModel.FORBID.getType().equals(defGlobalConfig.getOtherConfig().getSensitiveWordProcessModel())) {
            //不允许提交则抛出异常
            final Set<String> sensitiveWords = getSensitiveWords(createReqVO);
            if (CollUtil.isNotEmpty(sensitiveWords)) {
                throw exception0(SENSITIVEWORD_FORBID.getCode(), StrUtil.format("存在敏感词({})", JsonUtils.toJsonString(sensitiveWords)));
            }
        } else {
            //0:不提示直接替换; 1:提示之后替换
            String sensitiveReplaceWord = defGlobalConfig.getOtherConfig().getSensitiveReplaceWord();
            if (sensitiveReplaceWord == null) {
                sensitiveReplaceWord = StrUtil.EMPTY;
            }
            //标题
            String title = createReqVO.getTitle();
            final CommonResult<List<String>> listCommonResult = sensitiveWordApi.validateText((new SensitiveWordDto(title, SCENE_TAG_CONTENT_LIST)));
            if (listCommonResult.isSuccess()) {
                final List<String> sensitiveWords = listCommonResult.getData();
                if (CollUtil.isNotEmpty(sensitiveWords)) {
                    for (String sensitiveWord : sensitiveWords) {
                        //敏感词需要替换词
                        title = title.replace(sensitiveWord, sensitiveReplaceWord);
                    }
                    createReqVO.setTitle(title);
                }
            }
            //系统默认富文本
            if (StringUtils.isNotBlank(createReqVO.getText())) {
                final CommonResult<List<String>> textSenWordListResult = sensitiveWordApi.validateText((new SensitiveWordDto(createReqVO.getText(), SCENE_TAG_CONTENT_LIST)));
                if (textSenWordListResult.isSuccess()) {
                    final List<String> sensitiveWords = textSenWordListResult.getData();
                    String text = createReqVO.getText();
                    if (CollUtil.isNotEmpty(sensitiveWords)) {
                        for (String sensitiveWord : sensitiveWords) {
                            //敏感词需要替换词
                            text = text.replace(sensitiveWord, sensitiveReplaceWord);
                        }
                        createReqVO.setText(text);
                    }
                }
            }
            //摘要
            if (StringUtils.isNotBlank(createReqVO.getDescription())) {
                final CommonResult<List<String>> descriptSenWordListResult = sensitiveWordApi.validateText(new SensitiveWordDto(createReqVO.getDescription(), SCENE_TAG_CONTENT_LIST));
                if (descriptSenWordListResult.isSuccess()) {
                    final List<String> sensitiveWords = descriptSenWordListResult.getData();
                    String description = createReqVO.getDescription();
                    if (CollUtil.isNotEmpty(sensitiveWords)) {
                        for (String sensitiveWord : sensitiveWords) {
                            //敏感词需要替换词
                            description = description.replace(sensitiveWord, sensitiveReplaceWord);
                        }
                        createReqVO.setDescription(description);
                    }
                }
            }
            //作者
            if (StringUtils.isNotBlank(createReqVO.getAuthor())) {
                final CommonResult<List<String>> authorSenWordListResult = sensitiveWordApi.validateText(new SensitiveWordDto(createReqVO.getAuthor(), SCENE_TAG_CONTENT_LIST));
                if (authorSenWordListResult.isSuccess()) {
                    final List<String> sensitiveWords = authorSenWordListResult.getData();
                    String author = createReqVO.getAuthor();
                    if (CollUtil.isNotEmpty(sensitiveWords)) {
                        for (String sensitiveWord : sensitiveWords) {
                            //敏感词需要替换词
                            author = author.replace(sensitiveWord, sensitiveReplaceWord);
                        }
                        createReqVO.setAuthor(author);
                    }
                }
            }
            //子标题
            String subtitle = createReqVO.getSubtitle();
            final CommonResult<List<String>> subtitleCheckResult = sensitiveWordApi.validateText((new SensitiveWordDto(subtitle, SCENE_TAG_CONTENT_LIST)));
            if (subtitleCheckResult.isSuccess()) {
                final List<String> sensitiveWords = subtitleCheckResult.getData();
                if (CollUtil.isNotEmpty(sensitiveWords)) {
                    for (String sensitiveWord : sensitiveWords) {
                        //敏感词需要替换词
                        subtitle = subtitle.replace(sensitiveWord, sensitiveReplaceWord);
                    }
                    createReqVO.setSubtitle(subtitle);
                }
            }
            //自定义字段的敏感词替换
            final Map<String, String> inputCustomMap = getInputCustoms(createReqVO.getChannelId(), createReqVO.getCustoms());
            if (CollUtil.isNotEmpty(inputCustomMap)) {
                final Set<Map.Entry<String, String>> entries = inputCustomMap.entrySet();
                for (Map.Entry<String, String> entry : entries) {
                    if (StringUtils.isNotBlank(entry.getValue())) {
                        final CommonResult<List<String>> cusSenWordListResult = sensitiveWordApi.validateText((new SensitiveWordDto(entry.getValue(), SCENE_TAG_CONTENT_LIST)));
                        if (cusSenWordListResult.isSuccess()) {
                            final List<String> sensitiveWords = cusSenWordListResult.getData();
                            String cusVal = entry.getValue();
                            if (CollUtil.isNotEmpty(sensitiveWords)) {
                                for (String sensitiveWord : sensitiveWords) {
                                    cusVal = cusVal.replace(sensitiveWord, sensitiveReplaceWord);
                                }
                                //替换敏感词
                                createReqVO.getCustoms().put(entry.getKey(), cusVal);
                            }
                        }
                    }
                }
            }
        }
        return createReqVO;
    }

    public Set<String> getSensitiveWords(ContentBaseVO createReqVO) {
        Set<String> sensitiveWords = new HashSet<>();
        String toCheckTexts = StrUtil.join(createReqVO.getTitle(), createReqVO.getText(), createReqVO.getDescription(), createReqVO.getAuthor(), createReqVO.getSubtitle());
        //自定义字段文本类的和富文本的敏感词检查
        final Map<String, String> inputCustomMap = getInputCustoms(createReqVO.getChannelId(), createReqVO.getCustoms());
        if (CollUtil.isNotEmpty(inputCustomMap)) {
            final Set<Map.Entry<String, String>> entries = inputCustomMap.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                if (StringUtils.isNotBlank(entry.getValue())) {
                    toCheckTexts = StrUtil.join(toCheckTexts, entry.getValue());
                }
            }
        }
        final CommonResult<List<String>> titleValidResult = sensitiveWordApi.validateText((new SensitiveWordDto(toCheckTexts, SCENE_TAG_CONTENT_LIST)));
        if (titleValidResult.isSuccess()) {
            sensitiveWords.addAll(titleValidResult.getData());
        }
        return sensitiveWords;
    }


    /**
     * 获取内容 自定义字段数据是文本 输入类型的集合
     *
     * @param channelId
     * @param customs
     * @return
     */
    private Map<String, String> getInputCustoms(Long channelId, Map<String, Object> customs) {
        Map<String, String> inputCustomMap = new HashMap<>();
        if (!customs.isEmpty()) {
            ChannelDO channelDO = channelMapper.selectById(channelId);
            ModelDO modelDO = modelMapper.selectById(channelDO.getContentModelId());
            //拼接对象暂时用栏目id代替，只是为了检测是否有敏感词用
            List<ContentAttrDO> customDos = ContentAttrConvert.INSTANCE.disassembleCustoms(modelDO, channelId, customs);
            for (ContentAttrDO c : customDos) {
                if (TYPE_TEXT.equals(c.getAttrType()) || TYPE_EDITOR.equals(c.getAttrType()) || TYPE_TEXTAREA.equals(c.getAttrType())) {
                    inputCustomMap.put(c.getName(), c.getAttrValue());
                }
            }
        }
        return inputCustomMap;
    }

    @Resource
    private SensitiveWordApi sensitiveWordApi;
    @Resource
    private GlobalConfigService globalConfigService;
    @Resource
    private ModelMapper modelMapper;
    @Resource
    private ChannelMapper channelMapper;
}
